<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>语音电话 - LangChainGraph</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
        }

        .container {
            background: white;
            border-radius: 20px;
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
            width: 100%;
            max-width: 500px;
            overflow: hidden;
        }

        .header {
            background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
            color: white;
            padding: 30px;
            text-align: center;
        }

        .header h1 {
            font-size: 28px;
            font-weight: 600;
            margin-bottom: 10px;
        }

        .header p {
            opacity: 0.9;
            font-size: 16px;
        }

        .content {
            padding: 30px;
        }

        .call-controls {
            display: flex;
            flex-direction: column;
            gap: 20px;
        }

        .input-group {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }

        .input-group label {
            font-weight: 600;
            color: #333;
            font-size: 14px;
        }

        .input-group input {
            padding: 12px 16px;
            border: 2px solid #e1e5e9;
            border-radius: 10px;
            font-size: 16px;
            transition: border-color 0.3s ease;
        }

        .input-group input:focus {
            outline: none;
            border-color: #4facfe;
        }

        .btn {
            padding: 15px 20px;
            border: none;
            border-radius: 10px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
        }

        .btn-primary {
            background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
            color: white;
        }

        .btn-primary:hover {
            transform: translateY(-2px);
            box-shadow: 0 8px 20px rgba(79, 172, 254, 0.3);
        }

        .btn-danger {
            background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
            color: white;
        }

        .btn-danger:hover {
            transform: translateY(-2px);
            box-shadow: 0 8px 20px rgba(255, 107, 107, 0.3);
        }

        .btn:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none;
        }

        .status {
            text-align: center;
            padding: 15px;
            border-radius: 10px;
            margin: 20px 0;
            font-weight: 500;
        }

        .status.connecting {
            background: #fff3cd;
            color: #856404;
            border: 1px solid #ffeaa7;
        }

        .status.connected {
            background: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }

        .status.error {
            background: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }

        .call-info {
            background: #f8f9fa;
            padding: 20px;
            border-radius: 10px;
            margin: 20px 0;
        }

        .call-info h3 {
            margin-bottom: 15px;
            color: #333;
            font-size: 18px;
        }

        .participants {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
        }

        .participant {
            background: white;
            padding: 10px 15px;
            border-radius: 20px;
            border: 2px solid #e9ecef;
            font-size: 14px;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .participant.speaking {
            border-color: #4facfe;
            background: #e3f2fd;
        }

        .audio-visualizer {
            height: 60px;
            background: #f8f9fa;
            border-radius: 10px;
            margin: 20px 0;
            display: flex;
            align-items: center;
            justify-content: center;
            overflow: hidden;
        }

        .visualizer-bars {
            display: flex;
            align-items: end;
            gap: 2px;
            height: 40px;
        }

        .visualizer-bar {
            width: 3px;
            background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
            border-radius: 2px;
            transition: height 0.1s ease;
        }

        .ai-response {
            background: #e3f2fd;
            padding: 15px;
            border-radius: 10px;
            margin: 20px 0;
            border-left: 4px solid #4facfe;
        }

        .ai-response h4 {
            color: #1976d2;
            margin-bottom: 10px;
            font-size: 16px;
        }

        .ai-response p {
            color: #333;
            line-height: 1.5;
        }

        .hidden {
            display: none;
        }

        .loading {
            display: inline-block;
            width: 20px;
            height: 20px;
            border: 3px solid #f3f3f3;
            border-top: 3px solid #4facfe;
            border-radius: 50%;
            animation: spin 1s linear infinite;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        @media (max-width: 600px) {
            .container {
                margin: 10px;
                border-radius: 15px;
            }
            
            .header {
                padding: 20px;
            }
            
            .content {
                padding: 20px;
            }
            
            .btn {
                padding: 12px 16px;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🎵 语音电话</h1>
            <p>与AI助手进行实时语音对话</p>
        </div>

        <div class="content">
            <div id="setup-section">
                <div class="input-group">
                    <label for="user-id">用户ID</label>
                    <input type="text" id="user-id" placeholder="请输入用户ID" value="user-001">
                </div>

                <div class="input-group">
                    <label for="call-id">通话ID（可选）</label>
                    <input type="text" id="call-id" placeholder="留空将创建新通话">
                </div>

                <button class="btn btn-primary" onclick="startCall()" style="margin-top: 12px;">
                    <span id="start-icon">📞</span>
                    <span id="start-text">开始通话</span>
                </button>
            </div>

            <div id="call-section" class="hidden">
                <div class="status" id="call-status">正在连接...</div>

                <div class="call-info">
                    <h3>通话信息</h3>
                    <div class="participants" id="participants-list">
                        <div class="participant">
                            <span>👤</span>
                            <span id="current-user"></span>
                        </div>
                    </div>
                </div>

                <div class="audio-visualizer">
                    <div class="visualizer-bars" id="visualizer"></div>
                </div>

                <div class="ai-response hidden" id="ai-response">
                    <h4>🤖 AI回复</h4>
                    <p id="ai-text"></p>
                </div>

                <div class="call-controls">
                    <button class="btn btn-danger" onclick="endCall()">
                        <span>📴</span>
                        <span>结束通话</span>
                    </button>
                </div>
            </div>
        </div>
    </div>

    <script>
        let websocket = null;
        let mediaRecorder = null;
        let audioContext = null;
        let analyser = null;
        let isRecording = false;
        let callId = '';

        // 初始化音频可视化器
        function initVisualizer() {
            const visualizer = document.getElementById('visualizer');
            visualizer.innerHTML = '';
            for (let i = 0; i < 32; i++) {
                const bar = document.createElement('div');
                bar.className = 'visualizer-bar';
                bar.style.height = '2px';
                visualizer.appendChild(bar);
            }
        }

        // 更新音频可视化
        function updateVisualizer(dataArray) {
            const bars = document.querySelectorAll('.visualizer-bar');
            bars.forEach((bar, i) => {
                const value = dataArray[i] / 255;
                const height = Math.max(2, value * 40);
                bar.style.height = height + 'px';
            });
        }

        // 开始通话
        async function startCall() {
            const userId = document.getElementById('user-id').value.trim();
            const inputCallId = document.getElementById('call-id').value.trim();
            
            if (!userId) {
                alert('请输入用户ID');
                return;
            }

            try {
                // 初始化音频
                await initAudio();
                
                // 创建或加入通话
                if (inputCallId) {
                    callId = inputCallId;
                    await joinCall(userId, callId);
                } else {
                    await createCall(userId);
                }

                // 显示通话界面
                document.getElementById('setup-section').classList.add('hidden');
                document.getElementById('call-section').classList.remove('hidden');
                document.getElementById('current-user').textContent = userId;
                
                updateStatus('connected', '通话已连接');

            } catch (error) {
                console.error('开始通话失败:', error);
                updateStatus('error', '连接失败: ' + error.message);
            }
        }

        // 创建新通话
        async function createCall(userId) {
            const response = await fetch('/api/voice/call/start', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer ' + getAuthToken()
                },
                body: JSON.stringify({
                    participants: [userId]
                })
            });

            if (!response.ok) {
                throw new Error('创建通话失败');
            }

            const data = await response.json();
            callId = data.call_id;
            
            // 连接WebSocket
            await connectWebSocket(userId, callId);
        }

        // 加入现有通话
        async function joinCall(userId, callId) {
            // 连接WebSocket
            await connectWebSocket(userId, callId);
        }

        // 连接WebSocket
        function connectWebSocket(userId, callId) {
            return new Promise((resolve, reject) => {
                const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
                const wsUrl = `${protocol}//${window.location.host}/api/voice/call/ws/${callId}?user_id=${userId}`;
                
                websocket = new WebSocket(wsUrl);

                websocket.onopen = () => {
                    console.log('WebSocket连接已建立');
                    updateStatus('connected', '通话已连接');
                    resolve();
                };

                websocket.onmessage = (event) => {
                    const message = JSON.parse(event.data);
                    handleWebSocketMessage(message);
                };

                websocket.onerror = (error) => {
                    console.error('WebSocket错误:', error);
                    reject(new Error('WebSocket连接失败'));
                };

                websocket.onclose = () => {
                    console.log('WebSocket连接已关闭');
                    updateStatus('error', '连接已断开');
                    stopRecording();
                };
            });
        }

        // 处理WebSocket消息
        function handleWebSocketMessage(message) {
            console.log('收到消息:', message);
            
            switch (message.type) {
                case 'call_joined':
                    updateStatus('connected', '已加入通话');
                    break;
                    
                case 'user_joined':
                    addParticipant(message.user_id);
                    break;
                    
                case 'user_left':
                    removeParticipant(message.user_id);
                    break;
                    
                case 'user_speaking':
                    highlightParticipant(message.user_id);
                    break;
                    
                case 'speech_transcribed':
                    showTranscription(message.user_id, message.text);
                    break;
                    
                case 'ai_response':
                    showAIResponse(message.text, message.audio_data);
                    break;
                    
                case 'call_ended':
                    endCall();
                    break;
            }
        }

        // 初始化音频设备
        async function initAudio() {
            try {
                // 获取麦克风权限
                const stream = await navigator.mediaDevices.getUserMedia({ 
                    audio: {
                        echoCancellation: true,
                        noiseSuppression: true,
                        sampleRate: 16000
                    } 
                });

                // 创建音频上下文和分析器
                audioContext = new AudioContext();
                analyser = audioContext.createAnalyser();
                analyser.fftSize = 64;
                
                const source = audioContext.createMediaStreamSource(stream);
                source.connect(analyser);
                
                const dataArray = new Uint8Array(analyser.frequencyBinCount);
                
                // 初始化可视化器
                initVisualizer();
                
                // 更新可视化
                function update() {
                    if (analyser) {
                        analyser.getByteFrequencyData(dataArray);
                        updateVisualizer(dataArray);
                    }
                    requestAnimationFrame(update);
                }
                update();

                // 设置媒体录制器
                mediaRecorder = new MediaRecorder(stream, {
                    mimeType: 'audio/webm;codecs=opus',
                    audioBitsPerSecond: 16000
                });

                let audioChunks = [];

                mediaRecorder.ondataavailable = (event) => {
                    if (event.data.size > 0) {
                        audioChunks.push(event.data);
                    }
                };

                mediaRecorder.onstop = async () => {
                    const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
                    audioChunks = [];
                    
                    // 转换为base64
                    const reader = new FileReader();
                    reader.onload = () => {
                        const base64Audio = reader.result.split(',')[1];
                        sendAudioData(base64Audio);
                    };
                    reader.readAsDataURL(audioBlob);
                };

            } catch (error) {
                console.error('音频初始化失败:', error);
                throw new Error('无法访问麦克风');
            }
        }

        // 开始录音
        function startRecording() {
            if (mediaRecorder && mediaRecorder.state === 'inactive') {
                mediaRecorder.start(1000); // 每1秒触发一次dataavailable
                isRecording = true;
            }
        }

        // 停止录音
        function stopRecording() {
            if (mediaRecorder && mediaRecorder.state === 'recording') {
                mediaRecorder.stop();
                isRecording = false;
            }
        }

        // 发送音频数据
        function sendAudioData(audioData) {
            if (websocket && websocket.readyState === WebSocket.OPEN) {
                websocket.send(JSON.stringify({
                    type: 'audio_data',
                    audio_data: audioData,
                    timestamp: new Date().toISOString()
                }));
            }
        }

        // 结束通话
        function endCall() {
            if (websocket) {
                websocket.close();
            }
            
            stopRecording();
            
            // 重置界面
            document.getElementById('call-section').classList.add('hidden');
            document.getElementById('setup-section').classList.remove('hidden');
            document.getElementById('participants-list').innerHTML = `
                <div class="participant">
                    <span>👤</span>
                    <span id="current-user"></span>
                </div>
            `;
            
            // 清理资源
            if (audioContext) {
                audioContext.close();
                audioContext = null;
            }
            
            mediaRecorder = null;
            websocket = null;
            callId = '';
        }

        // 更新状态显示
        function updateStatus(type, message) {
            const statusEl = document.getElementById('call-status');
            statusEl.className = `status ${type}`;
            statusEl.textContent = message;
        }

        // 添加参与者
        function addParticipant(userId) {
            const participantsList = document.getElementById('participants-list');
            const participant = document.createElement('div');
            participant.className = 'participant';
            participant.id = `participant-${userId}`;
            participant.innerHTML = `<span>👤</span><span>${userId}</span>`;
            participantsList.appendChild(participant);
        }

        // 移除参与者
        function removeParticipant(userId) {
            const participant = document.getElementById(`participant-${userId}`);
            if (participant) {
                participant.remove();
            }
        }

        // 高亮正在说话的用户
        function highlightParticipant(userId) {
            const allParticipants = document.querySelectorAll('.participant');
            allParticipants.forEach(p => p.classList.remove('speaking'));
            
            const speakingParticipant = document.getElementById(`participant-${userId}`);
            if (speakingParticipant) {
                speakingParticipant.classList.add('speaking');
                setTimeout(() => {
                    speakingParticipant.classList.remove('speaking');
                }, 2000);
            }
        }

        // 显示语音转文本结果
        function showTranscription(userId, text) {
            console.log(`用户 ${userId} 说: ${text}`);
            // 可以在这里添加显示转录文本的逻辑
        }

        // 显示AI回复
        function showAIResponse(text, audioData) {
            const aiResponse = document.getElementById('ai-response');
            const aiText = document.getElementById('ai-text');
            
            aiText.textContent = text;
            aiResponse.classList.remove('hidden');
            
            // 播放AI语音回复
            if (audioData) {
                playAudio(audioData);
            }
        }

        // 播放音频
        function playAudio(base64Audio) {
            const audio = new Audio('data:audio/wav;base64,' + base64Audio);
            audio.play().catch(error => {
                console.error('播放音频失败:', error);
            });
        }

        // 获取认证令牌（简化实现）
        function getAuthToken() {
            // 在实际应用中，这里应该从localStorage或cookie中获取token
            return 'demo-token'; // 仅用于演示
        }

        // 自动开始录音（当连接建立后）
        function startRecording() {
            if (mediaRecorder && mediaRecorder.state === 'inactive') {
                mediaRecorder.start(1000); // 每1秒触发一次dataavailable
                isRecording = true;
            }
        }

        // 页面加载时初始化
        window.addEventListener('load', function() {
            console.log('语音电话界面已加载');
            // 检查浏览器支持
            if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
                alert('您的浏览器不支持语音功能，请使用现代浏览器如Chrome、Firefox或Edge');
            }
        });
    </script>
</body>
</html>
